#ifndef BASE_OBJECT_HPP
#define BASE_OBJECT_HPP

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Vector2.hpp>
#include <SFML/Graphics/Transformable.hpp>
#include <SFML/Graphics/Color.hpp>

#include <SmegTools/Config.h>
#include <SmegTools/Pattern/Clonable.hpp>

#include "../Math/Math.hpp"
#include "../Math/BoundingVolumes/BoundingVolume.hpp"
#include "../Game/MessageInfo.hpp"

#include <SmegTools/Debug/DebugNew.hpp>

#ifdef _DEBUG
namespace sf {
    class RenderWindow;
}
#endif

namespace smeg {
    namespace math {
        class QuadTreeNode;
        class QuadTree;
    }
    namespace core {
        
////////////////////////////////////////////////////////////
/// Represents a placeable object
////////////////////////////////////////////////////////////
class BaseObject : public tools::Clonable<BaseObject>, public sf::Transformable
{
public:
    ////////////////////////////////////////////////////////////
    /// Default Constructor
    ///
    ////////////////////////////////////////////////////////////
                            BaseObject();
    ////////////////////////////////////////////////////////////
    /// Copy Constructor
    ///
    /// \param _Copy :          Entity to copy from
    ///
    ////////////////////////////////////////////////////////////
                            BaseObject( const BaseObject& _Copy );
    ////////////////////////////////////////////////////////////
    /// Virtual Destructor
    ///
    ////////////////////////////////////////////////////////////
    virtual                 ~BaseObject();
        
    ////////////////////////////////////////////////////////////
    /// Update the entity
    ///
    /// \param _fDeltaTime :    Time since the last frame
    ///
    ////////////////////////////////////////////////////////////
    virtual void            Update( float _fDeltaTime ) = 0;

    ////////////////////////////////////////////////////////////
    /// Remove this object from the tree
    ///
    ////////////////////////////////////////////////////////////
    void                    RemoveFromQuadTree();    

    ////////////////////////////////////////////////////////////
    /// Handle An Incoming Message
    ///
    /// \param _Msg :           Message to handle
    ///
    ////////////////////////////////////////////////////////////
    virtual bool            HandleMessage( const MessageInfo& _Msg ) { return false; }
    
    ////////////////////////////////////////////////////////////
    /// Compute the bounding volume of the object (need to be overriden)
    ///
    ////////////////////////////////////////////////////////////
    virtual void            ComputeBoundingVolume();
    
    /*********************** Accessors ************************/
    bool                    HasBoundingVolume( math::BoundingVolume::eBoundingVolume _eType ) const;
    const math::BoundingVolume* GetBoundingVolume() const;
    
    void                    SetTree( math::QuadTree* _pTree );
    math::QuadTree*         GetTree() const;
    void                    SetNode( math::QuadTreeNode* _pNode );
    math::QuadTreeNode*     GetNode() const;

    void                    setPosition( float _fX, float _fY );
    void                    setPosition( const sf::Vector2f& _Move );

    void                    move( float _fX, float _fY );
    void                    move( const sf::Vector2f& _Move );

protected:
    ////////////////////////////////////////////////////////////
    /// If we are in a tree, call its update
    ///
    ////////////////////////////////////////////////////////////
    void                    QuadTreeUpdate();

    /******************* Protected members ********************/
    math::QuadTree*         m_pTree;                /// Tree we are in
    math::QuadTreeNode*     m_pNode;                /// Node we are in
    math::BoundingVolume*   m_pBoundingVolume;      /// Bounding Volume

#ifdef _DEBUG
public:
    virtual void            DebugRender( sf::RenderWindow* _pRenderWindow, const sf::Color& _Color = sf::Color::White ) const;
#endif // _DEBUG
};

/************************** Inline ************************/
inline bool BaseObject::HasBoundingVolume( math::BoundingVolume::eBoundingVolume _eType ) const {
    return _eType == math::BoundingVolume::eAny || (m_pBoundingVolume && m_pBoundingVolume->GetType() & _eType);
}

inline const math::BoundingVolume* BaseObject::GetBoundingVolume() const {
    return m_pBoundingVolume;
}

inline void BaseObject::SetTree( math::QuadTree* _pTree ) {
    m_pTree = _pTree;
}

inline math::QuadTree* BaseObject::GetTree() const {
    return m_pTree;
}

inline void BaseObject::SetNode( math::QuadTreeNode* _pNode ) {
    m_pNode = _pNode;
}

inline math::QuadTreeNode* BaseObject::GetNode() const {
    return m_pNode;
}

    } // namespace core
} // namespace smeg

#include <SmegTools/Debug/DebugNewOff.hpp>

#endif // BASE_OBJECT_HPP
